package com.lightboat.common.utils;


import cn.hutool.core.lang.Assert;
import com.lightboat.common.constant.StringPool;
import com.lightboat.common.core.domain.bo.ColumnBo;
import com.lightboat.common.core.domain.entity.SysDept;
import com.lightboat.common.function.SFunction;
import com.lightboat.common.serializable.LambdaMeta;
import com.lightboat.common.serializable.SerializedLambda;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ClassUtils;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


@Slf4j
public class LambdaUtils {



    public static ColumnBo getColumn(SFunction<?, ?> column) {
        LambdaMeta meta = LambdaUtils.extract(column);
        String methodName = meta.getImplMethodName();
        String fieldName = PropertyNamer.methodToProperty(methodName);
        return new ColumnBo(fieldName, StringUtils.toUnderScoreCase(fieldName));
    }


    /**
     * 解析Lambda表达式,加了缓存。
     * 该缓存可能会在任意不定的时间被清除
     *
     * @param lambda 需要解析的 lambda 对象
     * @param <T>    类型，被调用的 Function 对象的目标类型
     * @return 返回解析后的结果
     */
    public static <T> LambdaMeta extract(SFunction<T, ?> lambda) {
        Assert.notNull(lambda, "Lambda不能为null");

        // 如果是IDEA调试模式的代理对象，需要特殊处理
        if (lambda instanceof Proxy) {

            MethodHandle dmh = MethodHandleProxies.wrapperInstanceTarget(lambda);
            Executable executable = MethodHandles.reflectAs(Executable.class, dmh);

            return new LambdaMeta() {

                /**
                 * 获取 lambda 表达式实现方法的名称
                 *
                 * @return lambda 表达式对应的实现方法名称
                 */
                @Override
                public String getImplMethodName() {
                    return executable.getName();
                }

                /**
                 * 实例化该方法的类
                 *
                 * @return 返回对应的类名称
                 */
                @Override
                public Class<?> getInstantiatedClass() {
                    return executable.getDeclaringClass();
                }
            };
        }

        // 普通情况下的处理
        try {
            Method method = lambda.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(true);
            SerializedLambda invoke = (SerializedLambda) method.invoke(lambda);
            return getLambdaMeta(lambda, invoke);
        } catch (Exception e) {
            try {
                SerializedLambda invoke = SerializedLambda.extract(lambda);
                return getLambdaMeta(lambda, invoke);
            } catch (Exception ee) {
                throw new RuntimeException(ee);
            }
        }
    }

    /**
     * 获取 lambda 表达式中的元信息
     * @param lambda
     * @param invoke
     * @return
     * @param <T>
     */
    private static <T> LambdaMeta getLambdaMeta(SFunction<T, ?> lambda, SerializedLambda invoke) {
        return new LambdaMeta() {
            /**
             * 获取 lambda 表达式实现方法的名称
             *
             * @return lambda 表达式对应的实现方法名称
             */
            @Override
            public String getImplMethodName() {
                return invoke.getImplMethodName();
            }

            /**
             * 实例化该方法的类
             *
             * @return 返回对应的类名称
             */
            @Override
            public Class<?> getInstantiatedClass() {
                String instantiatedMethodType = invoke.getInstantiatedMethodType();
                String instantiatedType = instantiatedMethodType.substring(2, instantiatedMethodType.indexOf(StringPool.SEMICOLON)).replace(StringPool.SLASH, StringPool.DOT);
                return ClassUtils.resolveClassName(instantiatedType, lambda.getClass().getClassLoader());
            }
        };
    }

    public static void main(String[] args) {
        LambdaMeta extract = LambdaUtils.extract(SysDept::getDeptName);
        System.out.println(extract.getInstantiatedClass());
    }

}
